package com.jml.jdk8.stream;

import org.junit.Test;

import java.util.*;
import java.util.function.BiConsumer;
import java.util.function.BinaryOperator;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collector;

//list转化为map，中间容器是set
public class C自定义Collector2<T> implements
        Collector<T, Set<T>, Map<T, T>> {

    /**
     * 一个是提供新的容器
     */
    @Override
    public Supplier<Set<T>> supplier() {
        System.out.println("supplier invoked");
        return () -> {
            System.out.println("创建中间容器---");
            return new HashSet<T>();
        };
    }

    /**
     * 一个是不断往结果容器中追加内容
     */
    @Override
    public BiConsumer<Set<T>, T> accumulator() {
        System.out.println("accumulator invoked");
        //return (HashSet<T> set ,T t) ->set.add(t);
        return (Set<T> set, T t) -> {
            set.add(t);
            //System.out.println("Thread名字："
            //      + Thread.currentThread().getName()
            //      +" set"+set);
        };
        //return Set<T>::add;

        //不能用HashSet<T>::add;也不能用(HashSet<T> set ,T t) ->set.add(t);
        //因为接口定义的是Set<T>,supplier返回值是HashSet也可以是TreeSet，
        // 这里如果用具体的set进行add操作，类型可能会和supplier中定义的set不一致，
        // 所以只能用原生的set进行add，永远会和supplier中定义的set一致
        // return HashSet<T>::add;
    }

    /**
     * 一个是将并行流的结果容器合并到一起
     * 在并行流情况下，并且characteristics没有加CONCURRENT特性的时候才会调用，
     * 有CONCURRENT就不会调用
     */
    @Override
    public BinaryOperator<Set<T>> combiner() {
        System.out.println("combiner invoked");
        return (set1, set2) -> {
            System.out.println("合并中间结果集");
            set1.addAll(set2);
            return set1;
        };
    }

    /**
     * 一个是将累计结果类型转化为另一个结果类型
     * set转换为Map
     * 如果中间容器和返回容器一致就不会调用这个方法，
     * 会把中间容器进行强制转化为结果容器
     */
    @Override
    public Function<Set<T>, Map<T, T>> finisher() {
        System.out.println("finisher invoked");
        return set -> {
            //Map<T,T> map = new HashMap<>();
            Map<T, T> map = new TreeMap<>();
            set.stream().forEach(item -> map.put(item, item));
            return map;
        };
    }

    /**
     * 表示并行手机
     * IDENTITY_FINISH 表示中间容器和返回容器是同类型，不会调用finish方法，
     * 只会强制类型转化进行返回
     * UNORDERED  表示容器中的元素是无序的。
     *
     * @return
     */
    //返回的是一个Set的集合。这个集合表明结果容器的一些特性
    @Override
    public Set<Characteristics> characteristics() {
        System.out.println("characteristics invoked");
        //unmodifiableSet是不能修改的集合
        //如果设定为IDENTITY_FINISH，就不会调用finisher方法，
        //                 那么finisher中的方法可以写抛异常
        // 如果设定为IDENTITY_FINISH就会直接就返回中间容器，
        //                  把中间容器强转为最终的返回类型然后返回
        //UNORDERED 表示无序的，如果这里加了IDENTITY_FINISH运行的时候就会抛异常，
        //                  因为set无法强转map
        //CONCURRENT  表示多个线程同时操作同一个中间结果容器
        //           加CONCURRENT在并行流的情况下会偶尔出现并发修改异常
        //                  ConcurrentModificationException，冲突
        //           不加CONCURRENT在并行流的情况下，表示多个线程操作多个中间结果
        //                   容器，这是不冲突的不会报错
        //
        //检查是不是操作多个还是一个中间结果容器就看方法combiner，
        //          只有一个就不用掉方法进行合并，如果有多个就需要掉方法合并
        //如果加了CONCURRENT  就不要在accumulator方法中，
        //       一边进行add，一边进行遍历操作。打印就是遍历操作。否则就报错

        //表示多个线程同时操作同一个中间结果容器,出现并发修改异常,
        //                            不会调用combiner进行容器合并

        return Collections.unmodifiableSet(EnumSet.of(
                Characteristics.UNORDERED, Characteristics.CONCURRENT));

        //表示多个线程操作多个中间结果容器，会调用combiner
        //return Collections.unmodifiableSet(EnumSet.of( Characteristics.UNORDERED));
    }


    /**
     * list转化为map，中间容器是set
     */
    @Test
    public void list转化为map串行流() {
        List<String> list = Arrays.asList("Hello", "world", "welcome", "Hello");
        C自定义Collector2 myCollector = new C自定义Collector2();
        //单行流stream()
        //单行流stream().sequential()
        //如果把CONCURRENT删除，这里用串行流，
        //            supplier提供的中间结果容器有多少个？
        //一个
        Map<String, String> map = list.stream().sequential().collect(new C自定义Collector2<>());
        System.out.println(map);
    }

    /**
     * list转化为map，中间容器是set吗，parallelStream
     * characteristics中如果加了CONCURRENT会出现并发修改异常
     */
    @Test
    public void list转化为map并行流() {
        for (int i = 0; i < 1; i++) {
            System.out.println("-----------------------------------------------------------------------");
            List<String> list = Arrays.asList("Hello", "world", "welcome", "Hello");
            C自定义Collector2 myCollector = new C自定义Collector2();
            //parallelStream
            Map<String, String> map = list.parallelStream().collect(new C自定义Collector2<>());
            //stream().parallel()
            //Map<String,String>  map1 = list.stream().parallel().collect(new C自定义Collector2<>());
            //如果把CONCURRENT删除，这里用并行流，
            //       supplier提供的中间结果容器有多少个？
            //小于等于CPU核心数，具体和元素个数有关
            System.out.println(map);
        }
    }
}